package vip.xiaonuo.client.modular.wxorder.controller;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiOperation;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import vip.xiaonuo.auth.core.pojo.SaBaseLoginUser;
import vip.xiaonuo.auth.core.util.StpClientUtil;
import vip.xiaonuo.biz.modular.comment.param.BizCommentAddParam;
import vip.xiaonuo.biz.modular.comment.service.BizCommentService;
import vip.xiaonuo.biz.modular.order.entity.BizFzOrderDetail;
import vip.xiaonuo.biz.modular.order.entity.BizOrder;
import vip.xiaonuo.biz.modular.order.entity.BizUserTicket;
import vip.xiaonuo.biz.modular.order.param.BizOrderUpdateReservationTimeParam;
import vip.xiaonuo.biz.modular.order.mapper.BizFzOrderDetailMapper;
import vip.xiaonuo.biz.modular.order.param.BizTicketOrderAddParam;
import vip.xiaonuo.biz.modular.order.param.BizTicketOrderIdParam;
import vip.xiaonuo.biz.modular.order.param.BizTicketOrderPageParam;
import vip.xiaonuo.biz.modular.order.service.BizTicketOrderService;
import vip.xiaonuo.biz.modular.order.service.BizUserTicketService;
import vip.xiaonuo.biz.modular.spot.entity.Spot;
import vip.xiaonuo.biz.modular.spot.service.SpotService;
import vip.xiaonuo.biz.modular.ticket.entity.Ticket;
import vip.xiaonuo.biz.modular.ticket.service.TicketService;
import vip.xiaonuo.biz.modular.user.entity.BizFzUser;
import vip.xiaonuo.biz.modular.user.service.BizFzUserService;
import vip.xiaonuo.client.modular.wxpay.util.DingTalkUtil;
import vip.xiaonuo.client.modular.wxuser.entity.ClientUser;
import vip.xiaonuo.common.annotation.CommonLog;
import vip.xiaonuo.common.exception.CommonException;
import vip.xiaonuo.common.pojo.CommonResult;
import cn.hutool.core.date.DateUtil;

import javax.annotation.PostConstruct;
import javax.validation.Valid;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

@Slf4j
@Api(tags = "用户门票订单")
@ApiSupport(author = "SNOWY_TEAM", order = 1)
@RestController
@RequestMapping("/client/c/order")
public class OrederController {
    @Autowired
    private BizTicketOrderService bizTicketOrderService;

    @Autowired
    private BizCommentService bizCommentService;

    @Autowired
    private SpotService spotService;

    @Autowired
    private TicketService ticketService;

    @Autowired
    private BizUserTicketService bizUserTicketService;

    @Autowired
    private BizFzUserService bizFzUserService;

    @Autowired
    private BizFzOrderDetailMapper bizFzOrderDetailMapper;
    @Value("${ding_talk.webhook:}")
    private String dingTalkWebhook;

    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    @GetMapping("page")
    @ApiOperation("用户历史订单")
    @ApiOperationSupport(order = 1)
    public CommonResult<Page<BizOrder>> page(BizTicketOrderPageParam bizTicketOrderPageParam) {
        Optional.ofNullable(bizTicketOrderPageParam.getUserId()).orElseGet(() -> {
            ClientUser wxuser = JSONUtil.toBean(StpClientUtil.getSession().get("wxuser").toString(), ClientUser.class);
            bizTicketOrderPageParam.setUserId(wxuser.getId());
            return null;
        });
        return CommonResult.data(bizTicketOrderService.spotpage(bizTicketOrderPageParam));
    }

    @ApiOperationSupport(order = 2)
    @ApiOperation("下单")
    @CommonLog("添加门票订单表")
    @PostMapping("/add")
    public CommonResult<Map<String, Object>> add(@RequestBody @Valid BizTicketOrderAddParam bizTicketOrderAddParam) throws InterruptedException {
        try {
            // 验证用户信息完整性
            boolean match = bizTicketOrderAddParam
                    .getUserTickInfos()
                    .stream()
                    .allMatch(user -> ObjectUtil.isAllNotEmpty(user.getUserName(), user.getUserPhone()));
            if (!match) {
                throw new CommonException("用户信息输入不完整");
            }

            // 判断订单下单票价是否与后台票价一致
            Ticket ticket = ticketService.getById(bizTicketOrderAddParam.getTicketId());
            // 后台从库查询总价
            BigDecimal totalPrice = ticket.getPrice().multiply(new BigDecimal(bizTicketOrderAddParam.getTicketCount()));
            // 前端传入总价
            if (!totalPrice.equals(bizTicketOrderAddParam.getPrice())) {
                log.error("创建订单失败，票面价为：{}， 总价应为：{}; 传入总价为：{}", ticket.getPrice(), totalPrice, bizTicketOrderAddParam.getPrice());
                bizTicketOrderAddParam.setPrice(totalPrice);
//                return CommonResult.error("下单价格异常，请重新刷新小程序后，再次下单谢谢！");
            }

            // 获取当前登录用户
            ClientUser wxuser = JSONUtil.toBean(StpClientUtil.getSession().get("wxuser").toString(), ClientUser.class);
            bizTicketOrderAddParam.setUserId(wxuser.getId());
            bizTicketOrderAddParam.setStatus("待支付");
            bizTicketOrderAddParam.setPayMode("微信支付");
            if (StrUtil.isNotBlank(wxuser.getInvitationUserId())) {
                bizTicketOrderAddParam.setInvitationUserId(wxuser.getInvitationUserId());
            }

//            Ticket ticket = ticketService.getById(bizTicketOrderAddParam.getTicketId());
            bizTicketOrderAddParam.setBookTime(ticket.getBookTime());


            // 创建预订单，生成订单ID
            String uniqueOrderId = generateUniqueOrderId(wxuser.getId());
            bizTicketOrderAddParam.setId(uniqueOrderId);

            // 创建订单（此时状态为待支付）
            bizTicketOrderService.add(bizTicketOrderAddParam);

            log.info("创建订单成功，订单ID: {}", uniqueOrderId);

            // 构建返回信息
            Map<String, Object> responseMap = new HashMap<>();
            responseMap.put("orderId", uniqueOrderId);
            responseMap.put("ticketName", bizTicketOrderAddParam.getTicketName());
            responseMap.put("price", totalPrice);

            return CommonResult.data(responseMap);
        } catch (Exception e) {
            log.error("创建订单失败", e);
            throw new CommonException("创建订单失败: " + e.getMessage());
        }
    }

    /**
     * 生成唯一的订单ID
     * 格式: 用户ID后4位 + 时间戳 + 3位随机数
     */
    private String generateUniqueOrderId(String userId) {
        String userIdSuffix = userId.length() > 4 ?
                userId.substring(userId.length() - 4) :
                userId;

        return userIdSuffix +
               DateUtil.format(DateUtil.date(), "yyyyMMddHHmmss") +
               String.format("%03d", (int)(Math.random() * 1000));
    }

    //创建一个异步处理评论任务的机制。当有新的评论任务时，会添加到一个阻塞队列中。一个单独的线程会不断从队列中取出任务并处理
    private BlockingQueue<BizCommentAddParam> commentTasks = new ArrayBlockingQueue<>(1024 * 1024);
    private static final ExecutorService COMMENT_UPDATE_EXECUTOR = Executors.newSingleThreadExecutor();

    public void addCommentTask(BizCommentAddParam commentTask) {
        commentTasks.offer(commentTask);
    }

    private void processCommentTask(BizCommentAddParam commentTask) {
        Spot orderByspotName = spotService.getOrderByspotName(commentTask.getOrderId());
        updateSpotScore(orderByspotName.getId(), commentTask.getScore());
    }

    @PostConstruct
    public void init() {
        COMMENT_UPDATE_EXECUTOR.submit(() -> {
            while (true) {
                try {
                    BizCommentAddParam commentTask = commentTasks.take();
                    processCommentTask(commentTask);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        });
    }

    @ApiOperationSupport(order = 3)
    @ApiOperation("对订单评论")
    @PostMapping("addcomment")
    public CommonResult<String> addComment(@RequestBody @Valid BizCommentAddParam bizCommentAddParam) {
        ClientUser wxuser = JSONUtil.toBean(StpClientUtil.getSession().get("wxuser").toString(), ClientUser.class);
        bizCommentAddParam.setUserId(wxuser.getId());
        bizCommentService.add(bizCommentAddParam);

//        Spot orderByspotName = spotService.getOrderByspotName(bizCommentAddParam.getOrderId());
//        updateSpotScore(Long.valueOf(orderByspotName.getId()), bizCommentAddParam.getScore());

        this.addCommentTask(bizCommentAddParam);

        bizTicketOrderService.lambdaUpdate().eq(BizOrder::getId, bizCommentAddParam.getOrderId()).set(BizOrder::getStatus,"已评价").update();
        return CommonResult.ok();
    }

    @ApiOperationSupport(order = 4)
    @ApiOperation("获取订单详情")
    @GetMapping("detail")
    public CommonResult<BizOrder> detail(BizTicketOrderIdParam id) {
        return CommonResult.data(bizTicketOrderService.detail(id));
    }

    @ApiOperationSupport(order = 5)
    @ApiOperation("游客信息")
    @GetMapping("userinfo")
    public CommonResult<List<BizUserTicket>> userinfo(String orderId) {
        return CommonResult.data(bizUserTicketService.byorder(orderId));
    }

    @ApiOperationSupport(order = 6)
    @ApiOperation("更新订单预约时间")
    @PostMapping("updateReservationTime")
    public CommonResult<String> updateReservationTime(@RequestBody @Valid BizOrderUpdateReservationTimeParam param) {
        try {
            // 验证用户身份
            ClientUser wxuser = JSONUtil.toBean(StpClientUtil.getSession().get("wxuser").toString(), ClientUser.class);

            // 查询订单详情
            BizOrder order = bizTicketOrderService.detail(new BizTicketOrderIdParam() {{
                setId(param.getId());
            }});

            // 订单状态非已支付状态，禁止预约
            if (!"已支付".equals(order.getStatus())) {
                return CommonResult.error("订单还未支付，请支付后再次进行预约！");
            }

            // 验证订单归属
            if (!order.getUserId().equals(wxuser.getId())) {
                return CommonResult.error("无权修改此订单");
            }

            // 检查订单是否已存在预约时间
            if (order.getReservationTime() != null) {
                return CommonResult.error("订单已设置预约时间，不能再次修改");
            }

            // 更新预约时间，返回值表示操作是否成功
            boolean updateSuccess = bizTicketOrderService.updateReservationTime(param);

            if (updateSuccess) {
                order.setReservationTime(param.getReservationTime());
                sendOrderReservationTimeNotification(order);
                return CommonResult.ok("预约时间设置成功");
            } else {
                return CommonResult.error("预约时间设置失败，该订单可能已有预约时间");
            }
        } catch (Exception e) {
            log.error("更新预约时间失败", e);
            throw new CommonException("更新预约时间失败: " + e.getMessage());
        }
    }

    /**
     * 发送订单支付成功通知到钉钉
     */
    private void sendOrderReservationTimeNotification(BizOrder order) {
        if (StrUtil.isBlank(dingTalkWebhook)) {
            log.info("钉钉webhook未配置，跳过发送通知");
            return;
        }

        try {
            // 获取订单的下单人信息
            List<BizUserTicket> userTickets = bizUserTicketService.byorder(order.getId());
            String userName = "未知";
            String userPhone = "未知";
            if (userTickets != null && !userTickets.isEmpty()) {
                userName = userTickets.get(0).getUserName();
                userPhone = userTickets.get(0).getUserPhone();
            }

            // 构建消息内容
            StringBuilder content = new StringBuilder();
            content.append("【鑫享旅行订单】客户预约成功提醒\n\n");
            content.append("订单号: ").append(order.getId()).append("\n");
            content.append("下单人: ").append(userName).append("\n");
            content.append("手机号: ").append(userPhone).append("\n");
            content.append("景点名称: ").append(order.getSpotName() != null ? order.getSpotName() : "未知").append("\n");
            content.append("门票名称: ").append(order.getTicketName()).append("\n");
            content.append("购买数量: ").append(order.getTicketCount()).append("\n");
            content.append("订单金额: ").append(order.getPrice()).append("元\n");
            content.append("创建时间: ").append(DATE_FORMAT.format(order.getCreateTime())).append("\n");
            content.append("预约时间: ").append(DATE_FORMAT.format(order.getReservationTime()));

            // 发送钉钉消息
            DingTalkUtil.sendMessage(dingTalkWebhook, null, content.toString(), false);
        } catch (Exception e) {
            log.error("发送钉钉通知失败", e);
        }
    }
    @ApiOperationSupport(order = 7)
    @ApiOperation(value = "分销订单列表", notes = "查询当前登录用户作为分销用户邀请到的用户为其带来的分销订单列表及统计信息。\n" +
            "返回数据包含：\n" +
            "1. orders: 订单分页信息，每个订单包含commission字段表示当前用户的分佣金额\n" +
            "2. stats: 分销统计信息，包含：\n" +
            "   - totalOrders: 总订单数\n" +
            "   - totalAmount: 总金额\n" +
            "   - totalCommission: 总佣金\n" +
            "   - monthlyStats: 按月统计的订单金额\n\n" +
            "可能的错误：\n" +
            "- 如果当前用户不是分销用户，将返回空数据")
    @GetMapping("/distribution/page")
    public CommonResult<Map<String, Object>> distributionOrderPage(BizTicketOrderPageParam pageParam) {
        try {
            // 获取当前登录用户
            String currentUserId = StpClientUtil.getLoginIdAsString();
            // 手动写死分销用户id，用于本地接口测试时，查询固定分销用户的分销订单
//            String currentUserId = "1930932978919436289";

            // 1. 查询当前用户对应的分销用户信息 - 直接使用用户ID
            List<BizFzUser> userFzList = bizFzUserService.lambdaQuery()
                    .eq(BizFzUser::getId, currentUserId)
                    .list();

            if (userFzList == null || userFzList.isEmpty()) {
                Map<String, Object> emptyResult = new HashMap<>();
                emptyResult.put("fzOrders", new Page<>());
                return CommonResult.data(emptyResult);
            }

            // 2. 获取所有分销用户ID
            List<String> fzUserIds = userFzList.stream()
                    .map(BizFzUser::getId)
                    .collect(Collectors.toList());

            // 3. 查询下级分销用户
            List<BizFzUser> subFzUsers = bizFzUserService.lambdaQuery()
                    .in(BizFzUser::getParentId, fzUserIds)
                    .list();

            // 添加下级分销用户ID
            if (subFzUsers != null && !subFzUsers.isEmpty()) {
                List<String> subFzUserIds = subFzUsers.stream()
                        .map(BizFzUser::getId)
                        .collect(Collectors.toList());
                fzUserIds.addAll(subFzUserIds);
            }

            // 4. 查询所有与当前用户相关的分销订单
            // 查询条件：订单中的邀请人ID属于当前用户或其下级分销用户
            if (pageParam.getCurrent() == null || pageParam.getCurrent() <= 0) {
                pageParam.setCurrent(1);
            }
            if (pageParam.getSize() == null || pageParam.getSize() <= 0) {
                pageParam.setSize(10);
            }

            // 创建一个分页查询对象
            Page<BizOrder> page = new Page<>(pageParam.getCurrent(), pageParam.getSize());

            // 使用MyBatisPlus的lambda查询构建器
            Page<BizOrder> orderPage = bizTicketOrderService.lambdaQuery()
                    .in(BizOrder::getInvitationUserId, fzUserIds)
                    .ne(BizOrder::getStatus, "待支付") // 排除未支付的订单
                    .ne(BizOrder::getStatus, "已关闭") // 排除已关闭的订单
                    .ne(BizOrder::getStatus, "已退款") // 排除已退款的订单
                    .orderByDesc(BizOrder::getCreateTime)
                    .page(page);

            // 5. 为每个订单查询佣金信息并创建带佣金的订单列表
            List<OrderWithCommission> ordersWithCommission = new ArrayList<>();
            List<BizOrder> orderRecords = orderPage.getRecords();

            for (BizOrder order : orderRecords) {
                String orderId = order.getId();

                // 从分账订单明细表中查询该订单对应当前用户的分佣记录
                LambdaQueryWrapper<BizFzOrderDetail> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.eq(BizFzOrderDetail::getOrderId, orderId)
                           .eq(BizFzOrderDetail::getUserId, currentUserId).last("limit 1");

                BizFzOrderDetail fzOrderDetail = bizFzOrderDetailMapper.selectOne(queryWrapper);

                // 创建带佣金的订单对象
                OrderWithCommission orderWithCommission = new OrderWithCommission();
                BeanUtil.copyProperties(order, orderWithCommission);

                // 设置佣金金额（如果存在分账明细记录）
                if (fzOrderDetail != null) {
                    orderWithCommission.setCommission(fzOrderDetail.getMoney());
                } else {
                    orderWithCommission.setCommission(BigDecimal.ZERO);
                }

                ordersWithCommission.add(orderWithCommission);
            }

            // 6. 创建一个新的分页对象，包含带佣金的订单
            Page<OrderWithCommission> resultPage = new Page<>();
            BeanUtil.copyProperties(orderPage, resultPage, "records");
            resultPage.setRecords(ordersWithCommission);

            //// 7. 计算统计数据
            //Map<String, Object> statsMap = new HashMap<>();
            //if (!orderRecords.isEmpty()) {
            //    // 计算总订单数和总订单金额
            //    int totalOrders = orderRecords.size();
            //    BigDecimal totalAmount = orderRecords.stream()
            //            .map(BizOrder::getPrice)
            //            .reduce(BigDecimal.ZERO, BigDecimal::add);
            //
            //    // 计算总佣金
            //    BigDecimal totalCommission = ordersWithCommission.stream()
            //            .map(OrderWithCommission::getCommission)
            //            .reduce(BigDecimal.ZERO, BigDecimal::add);
            //
            //    // 按月统计订单金额
            //    Map<String, BigDecimal> monthlyStats = new HashMap<>();
            //    Calendar calendar = Calendar.getInstance();
            //    for (BizOrder order : orderRecords) {
            //        calendar.setTime(order.getCreateTime());
            //        String monthKey = calendar.get(Calendar.YEAR) + "-" + (calendar.get(Calendar.MONTH) + 1);
            //
            //        if (monthlyStats.containsKey(monthKey)) {
            //            monthlyStats.put(monthKey, monthlyStats.get(monthKey).add(order.getPrice()));
            //        } else {
            //            monthlyStats.put(monthKey, order.getPrice());
            //        }
            //    }
            //
            //    statsMap.put("totalOrders", totalOrders);
            //    statsMap.put("totalAmount", totalAmount);
            //    statsMap.put("totalCommission", totalCommission);
            //    statsMap.put("monthlyStats", monthlyStats);
            //}

            // 8. 构建结果
            Map<String, Object> result = new HashMap<>();
            result.put("fzOrders", resultPage);
            //result.put("stats", statsMap);

            return CommonResult.data(result);
        } catch (Exception e) {
            log.error("查询分销订单失败", e);
            throw new CommonException("查询分销订单失败: " + e.getMessage());
        }
    }

    public double calculateBayesianAverage(int C, double m, int n, double xBar) {
        return (C * m + n * xBar) / (C + n);
    }
    //采用贝叶斯平均算法更新景点评分
    public void updateSpotScore(String spotId, Double userScore) {
        int C =bizCommentService. getPriorRatingCount();//先验评分数量，即在考虑当前评分之前已有的评分数量
        double m = bizCommentService.getAllSpotsAverageScore();//先验评分平均值，即在考虑当前评分之前所有评分的平均值。


        double currentSpotScore = bizCommentService.getSpotScore(spotId);
        int ratingCount = bizCommentService.getRatingCount(spotId);//加1代表当前评分数量

        double newSpotScore = calculateBayesianAverage(C, m, ratingCount + 1, (currentSpotScore * ratingCount + userScore) / (ratingCount + 1));//xBar当前评分平均值，即新加入的评分和旧评分的平均值。
        spotService.updateSpotScoreInDatabase(spotId, newSpotScore);
    }

    /**
     * 带佣金信息的订单类
     */
    @Data
    public static class OrderWithCommission extends BizOrder {
        @ApiModelProperty(value = "分佣金额")
        private BigDecimal commission;
    }
}
